package main

import (
	"context"
	"flag"
	grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
	grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
	grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
	"github.com/grpc-ecosystem/grpc-gateway/runtime"
	"go.uber.org/zap"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/reflection"
	"google.golang.org/grpc/status"
	"gsms/api"
	"gsms/internal/conf"
	"gsms/pkg/mcontext"
	"gsms/pkg/tracing"
	"net"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"
)

var (
	customFunc grpc_recovery.RecoveryHandlerFunc
)


func main() {
	flag.Parse()
	ctx := context.Background()
	l := mcontext.Logger(ctx)
	if err := conf.Init(); err != nil {
		l.Panic("initial config failed", zap.Error(err))
	}
	_, closer := tracing.Init("gsms", nil)
	defer func() {
		if err:=closer.Close(); err!=nil{
			l.Panic("close io failed", zap.Error(err))
		}
	}()

	// create a listener on TCP port
	lis, err := net.Listen("tcp", conf.Conf.HTTP.RpcPort)
	if err != nil {
		l.Panic("failed to listen: %v", zap.Error(err))
	}

	//Define customfunc to handle panic
	customFunc = func(p interface{}) (err error) {
		return status.Errorf(codes.Unknown, "panic triggered: %v", p)
	}
	// Shared options for the logger, with a custom gRPC code to log level function.
	opts := []grpc_recovery.Option{
		grpc_recovery.WithRecoveryHandler(customFunc),
	}

	// create a service instance
	service, err := api.New(conf.Conf)
	if err != nil {
		l.Panic("failed to init service: %v", zap.Error(err))
	}
	grpcServer := grpc.NewServer(
		grpc_middleware.WithUnaryServerChain(
			grpc_zap.UnaryServerInterceptor(l),
			grpc_recovery.UnaryServerInterceptor(opts...),
		),
		grpc_middleware.WithStreamServerChain(
			grpc_zap.StreamServerInterceptor(l),
			grpc_recovery.StreamServerInterceptor(opts...),
		))

	// attach the service to the server
	api.RegisterSMSServer(grpcServer, service)
	reflection.Register(grpcServer)

	l.Info("rpc started")
	go func() {
		if err := grpcServer.Serve(lis); err != nil {
			l.Panic("failed to serve:", zap.Error(err))
		}
	}()

	go startHTTP()

	quit := make(chan os.Signal)
	signal.Notify(quit, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
	s := <-quit

	_, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	// 无限循环等待shutdown
	for {
		l.Info("gsms got a signal", zap.String("signal", s.String()))
		switch s {
		case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
			l.Info("Shutdown Server ...")
			grpcServer.GracefulStop()
			l.Info("Server exiting")
			api.Close(service, context.Background())
			return
		case syscall.SIGHUP:
		default:
			return
		}
	}

}

func startHTTP() {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	l := mcontext.Logger(ctx)
	// HTTP endpoint Connect to the GRPC server
	conn, err := grpc.Dial("localhost"+conf.Conf.HTTP.RpcPort, grpc.WithInsecure())
	if err != nil {
		l.Panic("fail to dial: ", zap.Error(err))
	}
	defer conn.Close()

	// Register grpc-gateway
	rmux := runtime.NewServeMux(runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{OrigName: true, EmitDefaults: true}))
	client := api.NewSMSClient(conn)
	err = api.RegisterSMSHandlerClient(ctx, rmux, client)
	if err != nil {
		l.Fatal("register client error: ", zap.Error(err))
	}
	err = http.ListenAndServe(conf.Conf.HTTP.Port, rmux)
	if err != nil {
		l.Fatal("start http server error: ", zap.Error(err))
	}
	l.Info("REST server ready...")
}

